package org.tea.application.browser.handler;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.CefLoadHandler;
import org.cef.handler.CefLoadHandlerAdapter;
import org.cef.network.CefRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.tea.application.configuration.CrawlServerConfiguration;
import org.tea.application.vo.ItemUrlVo;
import org.tea.common.ResponseResult;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

/**
 * @author lxh
 */
@Service
public class LoggingLoadHandler
        extends CefLoadHandlerAdapter
        implements CefLoadHandler {

    private final static Logger log = LoggerFactory.getLogger(LoggingLoadHandler.class);

    //    @Resource
    private JedisPool jedisPool;

    @Resource(name = "subscribeExecutor")
    private ExecutorService subscribeExecutor;

    @Resource
    private CrawlServerConfiguration crawlServerConfiguration;

    private final Map<String, Long> map = Maps.newConcurrentMap();

    @Override
    public void onLoadingStateChange(CefBrowser browser,
                                     boolean isLoading,
                                     boolean canGoBack,
                                     boolean canGoForward) {
        log.debug("loading state changed from {}", browser.getURL());
    }

    @Override
    public void onLoadStart(CefBrowser browser,
                            CefFrame frame,
                            CefRequest.TransitionType transitionType) {
        log.info("start load {}", browser.getURL());
    }

    @Override
    public void onLoadEnd(CefBrowser browser,
                          CefFrame frame,
                          int httpStatusCode) {
        String url = browser.getURL();
        log.info("load finished from {}", url);

        browser.executeJavaScript(script().replaceAll("\\$itemId",
                        String.valueOf(map.getOrDefault(url, 0L))),
                browser.getURL(), 0
        );

        try {
            String itemServerUrl = "http://" + crawlServerConfiguration.getHost()
                    + ":" + crawlServerConfiguration.getPort()
                    + "/" + crawlServerConfiguration.getDomServerPath()
                    + "/dom/nextItem";
            ResponseEntity<String> responseEntity =
                    new RestTemplate().postForEntity(itemServerUrl, null, String.class);
            ResponseResult<ItemUrlVo> result = new ObjectMapper().readValue(responseEntity.getBody(),
                    new TypeReference<ResponseResult<ItemUrlVo>>() {
                    });
            if (Objects.isNull(result.getData())) {
                System.exit(0);
                return;
            }

            ItemUrlVo urlVo = result.getData();
            log.info("next url: {}", urlVo.getUrl());
            browser.loadURL(urlVo.getUrl());
            map.put(urlVo.getUrl(), urlVo.getId());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            map.remove(browser.getURL());
        }
    }

    @Override
    public void onLoadError(CefBrowser browser,
                            CefFrame frame,
                            ErrorCode errorCode,
                            String errorText,
                            String failedUrl) {
        log.error("load [{}] failed, code: {}", browser.getURL(), errorCode);
    }

    static String script() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try (InputStream in = classLoader.getResourceAsStream("script/sync.js")) {
            byte[] buffer = new byte[4096];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = in.read(buffer)) > 0) {
                sb.append(new String(buffer, 0, len));
            }
            return sb.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
